#include "qbs.h"
#include "qbsconstants.h"

#include <coreplugin/icore.h>
#include <coreplugin/icontext.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/coreconstants.h>

#include <QAction>
#include <QMessageBox>
#include <QMainWindow>
#include <QMenu>
#include <QActionGroup>

#include <QTextDocument>
#include <QPlainTextEdit>

#include <coreplugin/editormanager/editormanager.h>

#include <coreplugin/find/currentdocumentfind.h>
#include <QTextEdit>

namespace Qbs {
namespace Internal {

QbsPlugin::QbsPlugin()
{
    // Create your members
    file_postfix_list << "c" << "h" << "cpp" << "hpp" << "hh" << "cc" << "c++" << "py" << "vim" << "el" << "sh" << "ts" << "json";
    //                    0       1      2        3         4      5       6        7       8       9        10

    client=new QTcpSocket();
}

QbsPlugin::~QbsPlugin()
{
    // Unregister objects from the plugin manager's object pool
    // Delete members
}


QString QbsPlugin::getRandomString(int length)
{

    //qsrand(QDateTime::currentMSecsSinceEpoch());
    srand(time(NULL));
    const char ch[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int size = sizeof(ch);
    char* str = new char[length + 1];
    int num = 0;
    for (int i = 0; i < length; ++i)
    {
        num = rand() % (size - 1);
        str[i] = ch[num];
    }
    //QString res(str);
    QString ret = QString(QLatin1StringView(str));

    return ret;
}



int QbsPlugin::getFiletypeByPostfix(const QString postfix){
    int ret = QBS::FILE_TYPE__UNKNOWN;
    switch ( file_postfix_list.indexOf(  postfix) ){
    case 0: case 1: case 2 : case 3: case 4: case 5: case 6: case 11: case 12:
        ret = QBS::FILE_TYPE__C_SERIES;
        break;
    case 7: case 8:
        ret  = QBS::FILE_TYPE__PYTHON;
        break;
    case 9:
        ret = QBS::FILE_TYPE__ELISP;
        break;
    case 10:
        ret = QBS::FILE_TYPE__SHELL;
        break;
    default:
        break;
    }
    return ret;
}


void QbsPlugin::setStringPart_a_and_b_according_filetype(int filetype){
    switch (filetype){
    case QBS::FILE_TYPE__UNKNOWN:
        QBS::part_a = "[";
        QBS::part_b = "]";
        break;
    case QBS::FILE_TYPE__C_SERIES:
        QBS::part_a = "//[";
        QBS::part_b = "]";
        break;
    case QBS::FILE_TYPE__PYTHON:
        QBS::part_a = "#[";
        QBS::part_b = "]";
        break;
    case QBS::FILE_TYPE__ELISP:
        QBS::part_a = ";[";
        QBS::part_b = "]";
        break;
    case QBS::FILE_TYPE__SHELL:
        QBS::part_a = "#[";
        QBS::part_b = "]";
        break;
     default:
        QBS::part_a = "[";
        QBS::part_b = "]";
        break;

    }
}

QString QbsPlugin::getRelativePathFilename(QString fullpath_filename){
    QString ret = "";
    if (fullpath_filename.startsWith(QBS::relative_path_1)){
        ret = fullpath_filename.last(   fullpath_filename.length() - QBS::relative_path_1_len  );
    }else     if (fullpath_filename.startsWith(QBS::relative_path_2)){
        ret = fullpath_filename.last(   fullpath_filename.length() - QBS::relative_path_2_len  );
    }

    if (ret == ""){

    }else{
        if (ret.at(0) == '/' ){
            ret.prepend(".");
        }else{
            ret.prepend("./");
        }
    }

    return ret;
}

int QbsPlugin::client_connect(){
    int ret = 0;
    client->connectToHost( QBS::host_address,  QBS::host_port_int);
    if (client->waitForConnected(3000)){
        // ok
        QObject::connect( client, &QTcpSocket::readyRead, this, &QbsPlugin::readData__client);
    }else{
        //failed
        ret = 1;
    }
    return ret;
}

void QbsPlugin::readData__client(){
    Core::IEditor * ied =        Core::EditorManager::currentEditor();
    QPlainTextEdit * pted = qobject_cast<QPlainTextEdit*>(ied->widget() );
    QString buf;
    buf = QString::fromUtf8(client->readAll());
    pted->insertPlainText(buf);
}

void QbsPlugin::writeData__client(QString data){
    client->write(data.toUtf8());
}

bool QbsPlugin::initialize(const QStringList &arguments, QString *errorString)
{

    QBS::host_port_int = QBS::host_port.toInt();

    char * relative_path_1_env = ::getenv("QBS_INSTALL_PATH_1" );
    if (relative_path_1_env != NULL){
        if (::strlen(relative_path_1_env) >= 2){
            QBS::relative_path_1 = QString::fromUtf8(relative_path_1_env );
        }
        //free(relative_path_1_env);
    }

    char * relative_path_2_env = ::getenv("QBS_INSTALL_PATH_2" );
    if (relative_path_2_env != NULL){
        if (::strlen(relative_path_2_env) >= 2){
            QBS::relative_path_2 = QString::fromUtf8(relative_path_2_env );
        }
        //free(relative_path_2_env);
    }

    QBS::relative_path_1_len = QBS::relative_path_1.length();
    QBS::relative_path_2_len = QBS::relative_path_2.length();

    client_connect();

    // Register objects in the plugin manager's object pool
    // Load settings
    // Add actions to menus
    // Connect to other plugins' signals
    // In the initialize function, a plugin can be sure that the plugins it
    // depends on have initialized their members.


    Q_UNUSED(arguments)
    Q_UNUSED(errorString)


    auto this_act_group = new QActionGroup(this);

    auto action = new QAction(tr("Qbs Action12"), this_act_group);
    Core::Command *cmd = Core::ActionManager::registerAction(action, QBS::Constants::ACTION_ID12,
                                                             Core::Context(Core::Constants::C_GLOBAL));
    //cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
    cmd->setDefaultKeySequence(QKeySequence(tr("f12")));
    connect(action, &QAction::triggered, this, &QbsPlugin::triggerAction12);

    Core::ActionContainer *menu = Core::ActionManager::createMenu(QBS::Constants::MENU_ID12);
    menu->menu()->setTitle(tr("Qbs-f12"));
    menu->addAction(cmd);
    Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);

    //this_act_group->addAction(cmd);

    auto action8 = new QAction(tr("Qbs Action8"), this_act_group);
    Core::Command *cmd8 = Core::ActionManager::registerAction(action8, QBS::Constants::ACTION_ID8,
                                                             Core::Context(Core::Constants::C_GLOBAL));
    //cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
    cmd8->setDefaultKeySequence(QKeySequence(tr("f8")));
    connect(action8, &QAction::triggered, this, &QbsPlugin::triggerAction8);

    Core::ActionContainer *menu8 = Core::ActionManager::createMenu(QBS::Constants::MENU_ID8);
    menu8->menu()->setTitle(tr("Qbs-f8"));
    menu8->addAction(cmd8);
    Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu8);

    auto action11 = new QAction(tr("Qbs Action11"), this_act_group);
    Core::Command *cmd11 = Core::ActionManager::registerAction(action11, QBS::Constants::ACTION_ID11,
                                                             Core::Context(Core::Constants::C_GLOBAL));
    //cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
    cmd11->setDefaultKeySequence(QKeySequence(tr("f11")));
    connect(action11, &QAction::triggered, this, &QbsPlugin::triggerAction11);

    Core::ActionContainer *menu11 = Core::ActionManager::createMenu(QBS::Constants::MENU_ID11);
    menu11->menu()->setTitle(tr("Qbs-f11"));
    menu11->addAction(cmd11);
    Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu11);

    return true;
}

void QbsPlugin::extensionsInitialized()
{
    // Retrieve objects from the plugin manager's object pool
    // In the extensionsInitialized function, a plugin can be sure that all
    // plugins that depend on it are completely initialized.
}

ExtensionSystem::IPlugin::ShutdownFlag QbsPlugin::aboutToShutdown()
{
    // Save settings
    // Disconnect from signals that are not needed during shutdown
    // Hide UI (if you add UI that is not in the main window directly)
    return SynchronousShutdown;
}






void QbsPlugin::triggerAction12()
{
    //QMessageBox::information(Core::ICore::mainWindow(), tr("Action Triggered"), tr("This is an action from Qbs--f12-."));

    Core::IDocument * ido =   Core::EditorManager::currentDocument();
    Core::IEditor * ied =        Core::EditorManager::currentEditor();
    int cur_pos_line = ied->currentLine();
    int cur_pos_col = ied->currentColumn();

    //QTextDocument * tdo = qobject_cast<QTextDocument*>(ido->) );  //err
     QPlainTextEdit * pted = qobject_cast<QPlainTextEdit*>(ied->widget() );
    //Core::IFindSupport::Result rt =   Core::IFindSupport::findIncremental("%%)", 0); //err
    bool found_end  = pted->find( "%%)", QTextDocument::FindCaseSensitively );
    if ( found_end ){
        int end_pos_line = ied->currentLine();
        int end_pos_col = ied->currentColumn();
        //return;  //debug
        bool found_start  = pted->find( "(%%", QTextDocument::FindCaseSensitively | QTextDocument::FindBackward );

        if (found_start){
            int start_pos_line = ied->currentLine();
            int start_pos_col = ied->currentColumn();
            ied->gotoLine( start_pos_line , start_pos_col -1 );
            QTextCursor tc = pted->textCursor();
            //pted->setCursor(qobject_cast<QCursor>(&tc) );
            tc.movePosition( QTextCursor::Down   , QTextCursor::KeepAnchor,  end_pos_line - start_pos_line );
            tc.movePosition( QTextCursor::StartOfLine,  QTextCursor::KeepAnchor);
            tc.movePosition( QTextCursor::Right,  QTextCursor::KeepAnchor, end_pos_col-4);
            QString data = tc.selectedText();
            //QMessageBox::information(Core::ICore::mainWindow(), tr("Action Triggered"),  "==" + data + "==");  //ok
            ied->gotoLine( end_pos_line , end_pos_col -1);
            //QMessageBox::information(Core::ICore::mainWindow(), tr("Action Triggered"), QString::number( end_pos_line) + QString::number(end_pos_col));
            writeData__client(data);

        }else{

        }


    }else{
        //can not find end, do nothing
    }

}
void QbsPlugin::triggerAction8()
{
    //QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),  tr("This is an action from Qbs--f8-."));
    //Core::ICore::findChild()
    //Core::ICore::mainWindow()
    //Core::ICore::
            ////(Core::ICore::mainWindow()->cursor()).pos()
            //(Core::ICore::mainWindow()->activateWindow(  ))->

    Core::IDocument * ido =   Core::EditorManager::currentDocument();
    Core::IEditor * ied =        Core::EditorManager::currentEditor();

    const Utils::FilePath fp = ido->filePath();
    //QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),(fp.baseName()   ));      //name without postfix
    //QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),(fp.completeBaseName()   )); //name without postfix
    //QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),(fp.toString()   ));            // fullpath filename  ok

      //QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),(fp.completeSuffix()   ));    //file postfix  ok
      //QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),(fp.fileNameWithPathComponents(0)   ));  //filename ok

      QString fullpath_fileanme = fp.toString() ;
      QString file_postfix = fp.completeSuffix() ;
      QString filename_without_path = fp.fileNameWithPathComponents(0);
      QString relative_filename = getRelativePathFilename(fullpath_fileanme);

      if (relative_filename.length() <= 2){
          QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),  QString::fromUtf8("the file must under the qbs install path:\n")  \
                                   + "(try: create a symlink to target dir under QBS install path, and then open file by following the symlink)" \
                                   + "\n\t1:" \
                                   +  QBS::relative_path_1 \
                                   + "\n\t2:" + QBS::relative_path_2  \
                                   + "\nif QBS install path is not listed above, need append env by: \n" \
                                   + "export QBS_INSTALL_PATH_1=\"/.../.../..\"\n"  \
                                   + "export QBS_INSTALL_PATH_2=\"~/.../...\"\n"  );
          return;
      }


      int filetype = getFiletypeByPostfix(file_postfix);
      setStringPart_a_and_b_according_filetype(filetype);


      int cur_pos_line = ied->currentLine();
      int cur_pos_col = ied->currentColumn();




      auto rs = getRandomString(20);
      //QMessageBox::information(Core::ICore::mainWindow(),tr("Action triggered f8"), rs);

      ied->gotoLine(cur_pos_line+1, 0);  //ok

       QPlainTextEdit * pted = qobject_cast<QPlainTextEdit*>(ied->widget() );
       pted->insertPlainText(QBS::part_a + rs + QBS::part_b + " (%%  ");
       cur_pos_line = ied->currentLine();
       cur_pos_col = ied->currentColumn();
       pted->insertPlainText(" %%)\n");
       ied->gotoLine(cur_pos_line , cur_pos_col );

       int client_is_ready = 0;
       if (client->state() == QAbstractSocket::ConnectedState){
           client_is_ready = 1;
       }else{
           client->close();
           client_connect();

       }

       if (client->state() == QAbstractSocket::ConnectedState){
           client_is_ready = 1;
       }else{
       }

       if (client_is_ready){
           //ok, client is ready, send data to server
            QString data = QBS::Constants::part_1 +  rs + QBS::Constants::part_2 + relative_filename + QBS::Constants::part_3 + QBS::Constants::part_4 + "c or cpp" + QBS::Constants::part_5 + QBS::Constants::part_6;
            writeData__client(data);
       }else{
           // client not ready, do nothing
           QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),  "can not connect to server:" +  QBS::host_address  + ":" + QBS::host_port);
           return;

       }


       //pted->insertPlainText(  "\n" );

    return;



    int fw_is_text_editor = 0;

    QWidget * fw = Core::ICore::mainWindow()->focusWidget();
    if (fw){
        if (  fw->isWidgetType()){
            //QMessageBox::information(Core::ICore::mainWindow(),tr("Action triggered f8"),tr(" foucus wdiget is a widget , not QTextEdit "));
            QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),tr(fw->metaObject()->className()      ));
            QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),tr(fw->metaObject()->superClass()->className()  ));
            const QMetaObject * qo = fw->metaObject()->superClass();
            if ( qo){
                printf(qo->className() );
                printf(fw->metaObject()->className() );

                if ( QLatin1String(qo->className()) == QLatin1String("QPlainTextEdit")  ){
                    QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered")," super is QPlainTextEdit ");
                    fw_is_text_editor = 101;
                }else{
                     QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered")," super not  QPlainTextEdit ");
                }
                if ( QLatin1String(qo->className()) == QLatin1String("TextEditor::TextEditorWidget")  ){
                    QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered")," super is TextEditor::TextEditorWidget ");
                    fw_is_text_editor = 102;
                }else{
                     QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered")," super not  TextEditor::TextEditorWidget ");
                }





                if ( QLatin1String( fw->metaObject()->className()) == QLatin1String("TextEditor::TextEditorWidget")){
                    QMessageBox::information(Core::ICore::mainWindow(),tr("Action triggered f8"), tr(" foucus wdiget is a widget , self is TextEditor::QTextEditWidget +++ "));
                    fw_is_text_editor = 2;
                }else{
                    QMessageBox::information(Core::ICore::mainWindow(),  tr("Action triggered f8"), tr(" foucus wdiget is a widget ,self not TextEditor::QTextEditWidget  ---- "));
                }


                if (fw_is_text_editor){
                    //ok is a editor
                    // 11 get QTextDocument * curtd;
                     QMessageBox::information(Core::ICore::mainWindow(),  tr("Actio"), tr("--1----"));
                    const QTextDocument * curtd;
                    const QPlainTextEdit * pt;
                    if (fw_is_text_editor == 101 ){
                        QMessageBox::information(Core::ICore::mainWindow(),  tr("Actio"), tr("--101 super is QPlainTextEdit----"));
                        //curtd = ((QPlainTextEdit *)(fw ))->document();
                        //curtd = ((QPlainTextEdit *)(fw ))->document(); //segment fault


                        pt = qobject_cast<QPlainTextEdit*>(fw );  //ok
                        //curtd = (qobject_cast<QPlainTextEdit*>(fw ))->document(); //segment fault
                        curtd = pt->document();
                    }else if (fw_is_text_editor == 2 ){

                        QMessageBox::information(Core::ICore::mainWindow(),  tr("Actio"), tr("-- 2 is TextEditor::TextEditorWidget----"));
                        //curtd = ((QTextEdit *)(fw ))->document();//segment fault
                        pt = qobject_cast<QPlainTextEdit*>(fw ); //ok
                        // pt = qobject_cast<QTextEdit*>(fw );//segment fault
                        curtd = pt->document();
                    }
                    QMessageBox::information(Core::ICore::mainWindow(),  tr("Actio"), tr("---4 QTextDocument is ready---"));
                    // 12 QTextDocument * curtd;  ready
                    if (curtd){
                        //QMessageBox::information(Core::ICore::mainWindow(),  tr("Action triggered f8. filename only..."), tr((curtd->baseUrl()).fileName().toStdString().c_str() )  );
                        QMessageBox::information(Core::ICore::mainWindow(),  tr("Action triggered f8. filename only..."), ((curtd->baseUrl()).fileName()));
                        QMessageBox::information(Core::ICore::mainWindow(),  tr("Action triggered f8. filename only..."), pt->documentTitle());
                    }

                    //auto rs = ::QString::
                    auto rs = getRandomString(20);
                    QMessageBox::information(Core::ICore::mainWindow(),tr("Action triggered f8"), rs);
                }else{
                    //not editor
                }


            }else{

            }

        }else{
            QMessageBox::information(Core::ICore::mainWindow(), tr("Action triggered f8"),  tr(" foucus wdiget is not a widget"));

        }
    }else{

        QMessageBox::information(Core::ICore::mainWindow(),  tr("Action triggered f8"),  tr(" foucus wdiget is NULL"));
    }


}

void QbsPlugin::triggerAction11()
{
    //QMessageBox::information(Core::ICore::mainWindow(),tr("Action Triggered"),tr("This is an action from Qbs--f11-."));


    Core::IDocument * ido =   Core::EditorManager::currentDocument();
    Core::IEditor * ied =        Core::EditorManager::currentEditor();
    int cur_pos_line = ied->currentLine();
    int cur_pos_col = ied->currentColumn();

    //QTextDocument * tdo = qobject_cast<QTextDocument*>(ido->) );  //err
     QPlainTextEdit * pted = qobject_cast<QPlainTextEdit*>(ied->widget() );
    //Core::IFindSupport::Result rt =   Core::IFindSupport::findIncremental("%%)", 0); //err
    bool found_start  = pted->find( "(%%", QTextDocument::FindCaseSensitively | QTextDocument::FindBackward);
    if ( found_start ){
        int start_pos_line = ied->currentLine();
        int start_pos_col = ied->currentColumn();
        //return;  //debug
        bool found_end  = pted->find( "%%)", QTextDocument::FindCaseSensitively  );

        if (found_end){
            int end_pos_line = ied->currentLine();
            int end_pos_col = ied->currentColumn();
            ied->gotoLine( start_pos_line , start_pos_col -1 );
            QTextCursor tc = pted->textCursor();
            //pted->setCursor(qobject_cast<QCursor>(&tc) );
            tc.movePosition( QTextCursor::Down   , QTextCursor::KeepAnchor,  end_pos_line - start_pos_line );
            tc.movePosition( QTextCursor::StartOfLine,  QTextCursor::KeepAnchor);
            tc.movePosition( QTextCursor::Right,  QTextCursor::KeepAnchor, end_pos_col-4);
            QString data = tc.selectedText();
            //QMessageBox::information(Core::ICore::mainWindow(), tr("Action Triggered"),  "==" + data + "==");  //ok
            ied->gotoLine( start_pos_line , start_pos_col -3 -1);
            //QMessageBox::information(Core::ICore::mainWindow(), tr("Action Triggered"), QString::number( end_pos_line) + QString::number(end_pos_col));
            writeData__client(data);

        }else{

        }


    }else{
        //can not find end, do nothing
    }



}

} // namespace Internal
} // namespace Qbs
